/*
 * Copyright (C) 2010 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.internal;

import java.util.*;
import org.osgi.framework.*;
import org.osgi.util.tracker.*;

/**
 * OSGi specific version of the {@link ServiceHelper}.
 * 
 * @author M. Hautle
 * @since 17.10.2010
 */
public class OSGiServiceHelper {
    /** The bundle context or null if not started. */
    private static BundleContext ctx;

    /** The open {@link ServiceTracker}s. */
    private static final List<ServiceTracker> TRACKERS = new ArrayList<ServiceTracker>();

    /**
     * Hidden constructor.
     */
    private OSGiServiceHelper() {
    }

    /**
     * Returns the first service provider for the given interface.
     * 
     * @param <T> The type of the service
     * @param <D> The default implementation type
     * @param service The service interface
     * @param defaultImpl The default/fallback implementation
     * @return The service to use
     */
    public static synchronized <T, D extends T> T getService(Class<T> service, Class<D> defaultImpl) {
        if (ctx == null)
            throw new IllegalStateException("The TrackedBean bundle is not yet started!");
        final T wrapper = ServiceWrapper.createWrapper(service, defaultImpl);
        final ServiceTracker t = new ServiceTracker(ctx, service.getName(), new Tracker(wrapper));
        t.open();
        // if there is not yet a service available - we set set the default impl
        if (t.getService() == null)
            ServiceWrapper.setImplementation(wrapper, null);
        return wrapper;
    }

    /**
     * Method 'activating' the helper - must be called by the bundle activator.
     * 
     * @param ctx The bundle context
     */
    public static synchronized void start(BundleContext ctx) {
        OSGiServiceHelper.ctx = ctx;
    }

    /**
     * Method stopping the helper - must be called by the bundle activator.
     */
    public static synchronized void stop() {
        ctx = null;
        for (ServiceTracker t : TRACKERS)
            t.close();
        TRACKERS.clear();
    }

    /**
     * {@link ServiceTrackerCustomizer} setting the service on a service wrapper.
     * 
     * @author M. Hautle
     * @since 17.10.2010
     */
    private static class Tracker implements ServiceTrackerCustomizer {
        /** The service wrapper. */
        private final Object service;

        /**
         * Default constructor.
         * 
         * @param service The service wrapper
         */
        private Tracker(Object service) {
            this.service = service;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Object addingService(ServiceReference reference) {
            final Object srv = ctx.getService(reference);
            ServiceWrapper.setImplementation(service, srv);
            return srv;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void modifiedService(ServiceReference reference, Object service) {
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void removedService(ServiceReference reference, Object service) {
            ServiceWrapper.setImplementation(service, null);
        }
    }
}
